#ifndef _USER_TABLE_
#define _USER_TABLE_

#include"util.hpp"

#define INSERT_USER "insert into user(name,account,password) values ('%s','%s','%s')"
#define LOGIN_USER "select id,name,score,total_count,win_count from user where account='%s' and password='%s'"
#define SELECT_ID_TO_NAME "select id from user where name='%s'"
#define SELECT_NAME "select id,score,total_count,win_count from user where name='%s'"
#define SELECT_ID "select name,score,total_count,win_count from user where id='%d'"
#define WIN_USER "update user set score=score+30,total_count=total_count+1,win_count=win_count+1 where id=%d"
#define LOSE_USER "update user set score=score-20,total_count=total_count+1 where id=%d"

class User_Table
{
public:
    User_Table(const std::string &host,const std::string &user,const std::string &pass,const std::string &name,uint16_t port=3306)
    {
        // 通过传入信息建立mysql连接
        _mysql=Mysql_Util::mysql_create(host,user,pass,name,port);
        assert(_mysql!=nullptr);
        // 通过传入信息建立redis连接
        _redis=Redis_Util::redis_connect(host,6379);
        assert(_redis!=nullptr);
    }

    // 用户注册
    bool insert(Json::Value &user)
    { 
        // 将传入的用户信息转为sql语句，并执行
        char sql[4096]={0};
        sprintf(sql,INSERT_USER,user["name"].asCString(),user["account"].asCString(),user["password"].asCString());
        bool ret=Mysql_Util::mysql_exec(_mysql,sql);
        if(ret==false)
        {
            ERR_LOG("input user info to mysql failed!");
            return false;
        }

        sprintf(sql,SELECT_ID_TO_NAME,user["name"].asCString());
        MYSQL_RES* res=nullptr;
        {
            Mysql_Util::mysql_exec(_mysql,sql);
            res=mysql_store_result(_mysql);
        }
        MYSQL_ROW row=mysql_fetch_row(res);
        user["id"]=row[0];

        // 将数据存入redis中
        user["name"]=user["name"];
        user["account"]=user["account"];
        user["password"]=user["password"];
        user["score"]=std::to_string(1000);
        user["total_count"]=std::to_string(0);
        user["win_count"]=std::to_string(0);
        ret=Redis_Util::redis_create(_redis,user);

        mysql_free_result(res);

        return true;
    }

    // 用户登录
    bool login(Json::Value &user)
    {
        int id=Redis_Util::redis_retrieve(_redis,user["account"].asString(),"password",user["password"].asString());
        if(id)
        {
            user["id"]=id;
            return true;
        }

        // 查找账号密码信息，返回用户其他对应信息
        char sql[4096]={0};
        sprintf(sql,LOGIN_USER,user["account"].asString().c_str(),user["password"].asString().c_str());

        // 创建结果集
        MYSQL_RES* res=nullptr;
        
        // 利用{}限定unique_lock的作用域，实现自动释放锁
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 将传入的用户信息转为sql语句，并执行
            bool ret=Mysql_Util::mysql_exec(_mysql,sql);
            if(ret==false)
            {
                ERR_LOG("user login failed!");
                return false;
            }

            // 结果只有为空，或者一行的情况
            res=mysql_store_result(_mysql);
            if(res==nullptr)
            {
                ERR_LOG("no login user info!");
                return false;
            }
        }
        int row_num=mysql_num_rows(res);
        if(row_num!=1)
        {
            ERR_LOG("user info queried is not unique!");
            return false;
        }

        // 遍历结果集, 并且这个接⼝会保存当前读取结果位置，每次获取的都是下⼀条数据
        // 将每⼀条数据做成了字符串指针数组，row[0]-第0列 row[1]-第1列
        MYSQL_ROW row=mysql_fetch_row(res);

        Json::Value redistmp;
        redistmp["id"]=row[0];
        redistmp["name"]=row[1];
        redistmp["account"]=user["account"];
        redistmp["password"]=user["password"];
        redistmp["score"]=row[2];
        redistmp["total_count"]=row[3];
        redistmp["win_count"]=row[4];
        Redis_Util::redis_create(_redis,redistmp);

        // id,name,score,total_count,win_count
        user["id"]=std::stoi(row[0]);
        user["name"]=row[1];
        user["score"]=std::stoi(row[2]);
        user["total_count"]=std::stoi(row[3]);
        user["win_count"]=std::stoi(row[4]);
        // 释放结果集
        mysql_free_result(res);
        return true;
    }

    // 通过name查找用户信息
    bool select_by_name(const std::string &name,Json::Value &user)
    {
        bool r=Redis_Util::redis_retrieve_by_name(_redis,name,user);
        if(r)return true;

        char sql[4096]={0};
        sprintf(sql,SELECT_NAME,name.c_str());
        MYSQL_RES* res=nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 将传入的用户信息转为sql语句，并执行
            bool ret=Mysql_Util::mysql_exec(_mysql,sql);
            if(ret==false)
            {
                ERR_LOG("get user by name failed!");
                return false;
            }

            res=mysql_store_result(_mysql);
            if(res==nullptr)
            {
                ERR_LOG("not find user info!");
                return false;
            }
        }

        int row_num=mysql_num_rows(res);
        if(row_num!=1)
        {
            ERR_LOG("user info queried is not unique!");
            return false;
        }

        MYSQL_ROW row=mysql_fetch_row(res);
        user["id"]=std::stoi(row[0]);
        user["name"]=name;
        user["score"]=std::stoi(row[1]);
        user["total_count"]=std::stoi(row[2]);
        user["win_count"]=std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    // 通过id查找用户信息
    bool select_by_id(const uint64_t &id,Json::Value &user)
    {
        DBG_LOG("id:%d",id);
        bool r=Redis_Util::redis_retrieve_by_id(_redis,std::to_string(id),user);
        if(r)return true;


        char sql[4096]={0};
        sprintf(sql,SELECT_ID,id);
        MYSQL_RES* res=nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret=Mysql_Util::mysql_exec(_mysql,sql);
            if(ret==false)
            {
                ERR_LOG("get user by name failed!");
                return false;
            }

            res=mysql_store_result(_mysql);
            if(res==nullptr)
            {
                ERR_LOG("not find user info!");
                return false;
            }
        }

        int row_num=mysql_num_rows(res);
        if(row_num!=1)
        {
            ERR_LOG("user info queried is not unique!");
            return false;
        }

        MYSQL_ROW row=mysql_fetch_row(res);
        user["id"]=(Json::UInt64)id;
        user["name"]=row[0];
        user["score"]=std::stoi(row[1]);
        user["total_count"]=std::stoi(row[2]);
        user["win_count"]=std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    // 用户获胜
    bool win(const uint64_t &id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        char sql[4096]={0};
        sprintf(sql,WIN_USER,id);
        bool ret=Mysql_Util::mysql_exec(_mysql,sql);
        if(ret==false)
        {
            ERR_LOG("update win user info failed!");
            return false;
        }

        Redis_Util::redis_update(_redis,std::to_string(id),true);

        return true;
    }

    // 用户失败
    bool lose(const uint64_t &id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        char sql[4096]={0};
        sprintf(sql,LOSE_USER,id);
        bool ret=Mysql_Util::mysql_exec(_mysql,sql);
        if(ret==false)
        {
            ERR_LOG("update lose user info failed!");
            return false;
        }

        Redis_Util::redis_update(_redis,std::to_string(id),false);

        return true;
    }

    ~User_Table()
    {
        Mysql_Util::mysql_destroy(_mysql);
        _mysql=nullptr;
    }
private:
    MYSQL* _mysql;
    sw::redis::Redis* _redis;
    std::mutex _mutex;
};

#endif